// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: Chen Xihao (chenxihao@baidu.com)
//
// Description: Media player API

#include "baidu_media_play.h"
#include "baidu_media_adapter.h"
#include "baidu_media_play_m4a.h"
#include "heap_monitor.h"
#include "duer_log.h"
#include "duer_play_command_manager.h"
#include "baidu_measure_stack.h"

namespace duer {

struct ListenerListNode {
    MediaPlayerListener* listener;
    ListenerListNode*    next;
};

struct ListenerList {
    rtos::Mutex mutex;
    ListenerListNode* first;
    ListenerListNode* last;
};

MediaPlayBuffer* g_media_play_buffer = NULL;

static const uint32_t MEDIA_PLAY_STACK_SIZE = 1024 * 4;
static Thread s_media_player_thread(/*osPriorityAboveNormal*/osPriorityAboveNormal, MEDIA_PLAY_STACK_SIZE);

static const uint32_t MEDIA_VOICE_STACK_SIZE = 1024;
static Thread s_media_voice_thread(/*osPriorityHigh*/osPriorityNormal, MEDIA_VOICE_STACK_SIZE);

static Semaphore s_volume_semaphore;
static signed char s_volume = 7;

static const int32_t S_RESUME_SIGNAL = 0x01;
static volatile MediaPlayerStatus s_media_player_status = MEDIA_PLAYER_IDLE;
// Is Mutex needed?
// static Mutex s_status_mutex;
static ListenerList s_listeners;

static MediaAdapter s_media_adapter;
static MediaPlayM4A* s_media_m4a = NULL;

static int s_write_codec_size = 0;
static MediaPlayM4A* s_previous_m4a = NULL;
static bool s_finished = false; // indicate last play finished or stopped halfway

void media_play_set_volume(unsigned char vol) {
    if (s_volume != vol) {
        s_volume = vol;
        s_volume_semaphore.release();
    }
}

unsigned char media_play_get_volume() {
	return s_volume;
}

static void media_voice_thread() {
    DUER_LOGI("init media_voice_thread.");
	DUER_LOGI("thread: id = %x, used = %d, max = %d", s_media_voice_thread.gettid(), s_media_voice_thread.used_stack(),s_media_voice_thread.max_stack());
    while (true) {
        s_volume_semaphore.wait();
        DUER_LOGI("set_volume %d", s_volume);
        s_media_adapter.regulate_voice(s_volume);
    }
}

static void on_media_player_start() {

	//////close by lijun
    //duer::PlayCommandManager::instance().start();

    s_listeners.mutex.lock();
    ListenerListNode* node = s_listeners.first;
    while (node != NULL) {
        node->listener->on_start();
        node = node->next;
    }
    s_listeners.mutex.unlock();
}

static void on_media_player_stop() {
	//////close by lijun
    //duer::PlayCommandManager::instance().stop();

    s_listeners.mutex.lock();
    ListenerListNode* node = s_listeners.first;
    while (node != NULL) {
        node->listener->on_stop();
        node = node->next;
    }
    s_listeners.mutex.unlock();
}

static void on_media_player_finish() {
	//////close by lijun
    //duer::PlayCommandManager::instance().end();

    s_listeners.mutex.lock();
    ListenerListNode* node = s_listeners.first;
    while (node != NULL) {
        node->listener->on_finish();
        node = node->next;
    }
    s_listeners.mutex.unlock();
}

static void handle_event_play() {
    static unsigned char buffer[FRAME_SIZE];

    MediaPlayerMessage media_message;
    g_media_play_buffer->read(&media_message, sizeof(media_message));
    int size = media_message.size;
    if (size > FRAME_SIZE) {
        DUER_LOGE("message's buffer size is bigger than FRAME_SIZE!");
        return;
    }

    if (size > 0) {
        g_media_play_buffer->read(buffer, size);
    }

    MediaType type = media_message.type;
    DUER_LOGV("commond:%d, type:%d, size:%d\n", media_message.cmd, type, size);
    DUER_LOGV("media player status:%d.", s_media_player_status);

    switch (media_message.cmd) {
    case PLAYER_CMD_START:
        s_write_codec_size = 0;
        if (MEDIA_PLAYER_IDLE == s_media_player_status) {
            DUER_LOGD("play start");
            s_media_player_status = MEDIA_PLAYER_PLAYING;
            s_finished = false;

            on_media_player_start();

            if (type == TYPE_M4A) {
                media_play_clear_previous_m4a();

                s_media_m4a = NEW(MEDIA) MediaPlayM4A(&s_media_adapter);
                if (NULL == s_media_m4a) {
                    DUER_LOGE("No free memory.");
                    break;
                }
                s_media_m4a->m4a_file_start(type, buffer, size);
            } else if (type == TYPE_M4A2) {
                DUER_LOGD("Continue play m4a");
                if (NULL == s_previous_m4a) {
                    DUER_LOGE("s_previous_m4a is null.");
                    break;
                }
                s_media_m4a = s_previous_m4a;
                s_previous_m4a = NULL;
                s_media_adapter.start_play(TYPE_M4A);
                s_media_m4a->m4a_file_play(buffer, size);
            } else if (type == TYPE_MP3 || type == TYPE_AAC || type == TYPE_WAV
                    || type == TYPE_SPEECH || type == TYPE_TS || type == TYPE_AMR) {
                s_media_adapter.start_play(type);
                s_media_adapter.write(buffer, size);
            } else {
                DUER_LOGE("media type error:%d.", type);
            }
            s_write_codec_size += size;
        } else {
            DUER_LOGE("PLAYER_CMD_START status error:%d!", s_media_player_status);
        }
        break;
    case PLAYER_CMD_STOP:
        if (MEDIA_PLAYER_IDLE != s_media_player_status) {
            //stop play, get queue till empty later
            if (type == TYPE_M4A || type == TYPE_M4A2) {
                if (NULL == s_media_m4a) {
					
                    DUER_LOGE("cmd stop s_media_m4a null.");
                    break;
                }
                s_media_m4a->m4a_file_end(buffer, size);
                delete s_media_m4a;
                s_media_m4a = NULL;
            } else if (type == TYPE_MP3 || type == TYPE_AAC || type == TYPE_WAV
                    || type == TYPE_SPEECH || type == TYPE_TS || type == TYPE_AMR) {
                s_media_adapter.write(buffer, size);
                s_media_adapter.write(NULL, 0);
            } else {
                DUER_LOGE("media type error:%d.", media_message.type);
            }

            s_write_codec_size += size;

            // wait till codec play end
            while (!s_media_adapter.is_play_stopped()) {
                Thread::wait(100);
            }

            // media_play_stop maybe called while waiting
            if (s_media_player_status != MEDIA_PLAYER_IDLE) {
                s_finished = true;
                s_media_player_status = MEDIA_PLAYER_IDLE;
                on_media_player_finish();	
				DUER_LOGD("play end");
            }

        } else {
            DUER_LOGD("media player halfway stop!\n");

            if (s_media_m4a != NULL) {
                media_play_clear_previous_m4a();

                s_previous_m4a = s_media_m4a;
                s_media_m4a = NULL;
            }
        }
        break;
    case PLAYER_CMD_CONTINUE:
        if (MEDIA_PLAYER_IDLE != s_media_player_status) {
            //play the data
            if (type == TYPE_M4A || type == TYPE_M4A2) {
                if (NULL == s_media_m4a) {
                    DUER_LOGE("cmd continue s_media_m4a null.");
                    break;
                }
                s_media_m4a->m4a_file_play(buffer, size);
            } else if (type == TYPE_MP3 || type == TYPE_AAC || type == TYPE_WAV
                    || type == TYPE_TS || type == TYPE_AMR) {

				
                s_media_adapter.write(buffer, size);	
				
            } else if (type == TYPE_SPEECH) {
                s_media_adapter.write(buffer, size);
                // reduce write codec frequency
                Thread::wait(300);
            } else {
                DUER_LOGE("media type error:%d.", media_message.type);
            }
            s_write_codec_size += size;
        } else {
            DUER_LOGV("media player halfway stop!\n");
        }
        break;
    default:
        DUER_LOGE("!!!media commond Error!!!");
        break;
    }

	//DUER_LOGI("s_write_codec_size = %d",s_write_codec_size);
	
    return;
}

static void pause_play() {
    s_media_adapter.pause_play();

    DUER_LOGI("media play thread pause\n");
    Thread::signal_wait(S_RESUME_SIGNAL);
    DUER_LOGI("media play thread resume\n");
}

static void media_play_thread() {
    DUER_LOGI("Entry Media_Play");
	DUER_LOGI("thread: id = %x, used = %d, max = %d", s_media_player_thread.gettid(), s_media_player_thread.used_stack(),s_media_player_thread.max_stack());
    while (true) {
        if (s_media_player_status == MEDIA_PLAYER_PAUSE) {
            pause_play();
        }
        handle_event_play();
    }
	 DUER_LOGI("Leave Media_Play");
}

MediaPlayerStatus media_play_pause_or_resume() {
    MediaPlayerStatus oldStatus = s_media_player_status;
    if (oldStatus == MEDIA_PLAYER_PAUSE) {
        s_media_player_thread.signal_set(S_RESUME_SIGNAL);
        s_media_player_status = MEDIA_PLAYER_PLAYING;
    } else if (oldStatus == MEDIA_PLAYER_PLAYING) {
        s_media_player_status = MEDIA_PLAYER_PAUSE;
    } else {
        //do nothing
    }
    return oldStatus;
}

MediaPlayerStatus media_play_stop(bool no_callback) {
    MediaPlayerStatus oldStatus = s_media_player_status;
    s_media_player_status = MEDIA_PLAYER_IDLE;
	
    s_media_adapter.stop_play();

	if (oldStatus == MEDIA_PLAYER_PAUSE) {
        s_media_player_thread.signal_set(S_RESUME_SIGNAL);
    }

    if (oldStatus != MEDIA_PLAYER_IDLE && !no_callback) {
        DUER_LOGI("play stop");
        on_media_player_stop();
    }

    return oldStatus;
}

void start_media_play_thread() {
#ifdef BAIDU_STACK_MONITOR
    register_thread(&s_media_player_thread, "MediaPlayer");
#endif
    s_media_player_thread.start(media_play_thread);
}

void start_media_voice_thread() {
#ifdef BAIDU_STACK_MONITOR
    register_thread(&s_media_voice_thread, "MediaVoice");
#endif
    s_media_voice_thread.start(media_voice_thread);
}

MediaPlayerStatus media_play_get_status() {
    return s_media_player_status;
}

int media_play_register_listener(MediaPlayerListener* listener) {
    if (listener == NULL) {
        DUER_LOGW("listener is NULL");
        return -1;
    }

    s_listeners.mutex.lock();
    // check whether register repeat
    ListenerListNode* node = s_listeners.first;
    while (node != NULL) {
        if (node->listener == listener) {
            s_listeners.mutex.unlock();
            DUER_LOGW("listener has been registered before.");
            return -1;
        }
        node = node->next;
    }

    node = (ListenerListNode*)MALLOC(sizeof(ListenerListNode), MEDIA);
    if (node == NULL) {
        s_listeners.mutex.unlock();
        DUER_LOGE("No free memory!");
        return -1;
    }

    node->listener = listener;
    node->next = NULL;

    if (s_listeners.last == NULL) {
        s_listeners.first = s_listeners.last = node;
    } else {
        s_listeners.last->next = node;
        s_listeners.last = node;
    }

    s_listeners.mutex.unlock();

    return 0;
}

int media_play_unregister_listener(MediaPlayerListener* listener) {
    if (listener == NULL) {
        DUER_LOGW("listener is NULL");
        return -1;
    }

    s_listeners.mutex.lock();

    ListenerListNode* node = s_listeners.first;
    ListenerListNode* prev = NULL;
    while (node != NULL) {
        if (node->listener == listener) {
            if (s_listeners.first == s_listeners.last) { // list has one item only
                s_listeners.first = s_listeners.last = NULL;
            } else if (prev == NULL) { // item is first node
                s_listeners.first = node->next;
            } else {
                prev->next = node->next;
                if (node == s_listeners.last) { // item is last node
                    s_listeners.last = prev;
                }
            }
            FREE(node);
            break;
        }

        prev = node;
        node = node->next;
    }
    s_listeners.mutex.unlock();

    return 0;
}

int media_play_get_write_size() {
    return s_write_codec_size;
}

int media_play_seek_m4a_file(int position) {
    if (s_previous_m4a != NULL) {
        return s_previous_m4a->m4a_file_seek(position);
    }

    return 0;
}

bool media_play_finished() {
    return s_finished;
}

void media_play_clear_previous_m4a() {
    if (s_previous_m4a != NULL) {
        delete s_previous_m4a;
        s_previous_m4a = NULL;
    }
}


void media_set_bt_mode()
{
	s_media_adapter.setBtMode();
}


void media_set_bt_name(const unsigned char* name)
{
	s_media_adapter.SetName(name);
}


void media_set_uart_mode()
{
	s_media_adapter.setUartMode();
}


} // namespace duer
